#include <QTextCodec>
#include "qmladb.h"


QMLAdb::QMLAdb(QDeclarativeItem *parent) :
    QDeclarativeItem(parent)
{
    m_state = Idle;
    m_adbPath = "";

    connect( &m_process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)) );
    connect( &m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processExited(int, QProcess::ExitStatus)) );
    connect( &m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)) );
}

QMLAdb::~QMLAdb()
{
}

void QMLAdb::readFromStdout()
{
    qDebug() << "QMLAdb::read stdout";

}
//プロセスの状態変化
void QMLAdb::processStateChanged(QProcess::ProcessState newState)
{
    qDebug() << "QMLAdb::processStateChanged " << newState;
    qDebug() << "  m_mode=" << m_mode << ", state=" << m_state << ", a len=" << m_tempWriteData.length();

    switch(newState){
    case QProcess::NotRunning:
        break;
    case QProcess::Starting:
        break;
    case QProcess::Running:
        //動き出した
        if(m_mode == ShellCommand && m_state == Executing && m_tempWriteData.length() > 0){
            qDebug() << "  write:" << m_tempWriteData;
            m_process.write(m_tempWriteData);
            m_process.closeWriteChannel();
            m_tempWriteData.clear();
        }
        break;
    default:
        break;
    }
}
//プロセスからの完了応答スロット
void QMLAdb::processExited(int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "QMLAdb::process exited , exitCode=" << exitCode << ", exitStatus=" << exitStatus;

    QString result;
    result = "";

    QByteArray output = m_process.readAllStandardOutput();
    QString str = QString::fromLocal8Bit( output );

    StateType state = Idle;
    QStringList list;
    switch(m_mode){
    case Push:
        list = parsePush(str);
        break;

    case Pull:
        break;

    case Devices:
        list = parseDevices(str);
        break;

    case ShellMkDir:
        //リストがなくなるまで繰り返し
        if(postMkDir()){
            state = Executing;
        }
        break;

    case ShellCommand:
        qDebug() << str;
        break;

    default:
        break;
    }

    m_state = state;
    emit finished(exitCode, m_mode, list);
}
//プロセスがエラーになった
void QMLAdb::processError(QProcess::ProcessError error)
{
    qDebug() << "QMLAdb::processError " << error;
    m_state = Idle;
    QStringList list;
    emit finished(1, m_mode, list);
}

/////////////////////////////////////////////////////////////
// setter/getter
/////////////////////////////////////////////////////////////

QString QMLAdb::adbPath() const
{
    return m_adbPath;
}
void QMLAdb::setAdbPath(const QString &adbpath)
{
    if(adbpath != m_adbPath){
        m_adbPath = adbpath;
        emit adbPathChanged();
    }
}
QString QMLAdb::deviceName() const
{
    return m_deviceName;
}
void QMLAdb::setDeviceName(const QString &devicename)
{
    if(devicename != m_deviceName){
        m_deviceName = devicename;
        emit deviceNameChanged();
    }
}

/////////////////////////////////////////////////////////////
// methods
/////////////////////////////////////////////////////////////

//Winの場合は/を\に直す
QString QMLAdb::fixPathChar(QString path)
{
#if defined(Q_OS_WIN)
    return path.replace("/", "\\");
#else
    return path;
#endif
}

//adb devicesを発行する
bool QMLAdb::postAdbDevices()
{
    qDebug() << "QMLAdb::getDevices";

    bool ret = false;
    if(m_state != Idle){
        qDebug() << "  already executing.";
    }else if(m_adbPath.length() < 1){
        qDebug() << "  empty adb path.";
    }else{
        QStringList args;

        m_state = Executing;
        m_mode = Devices;
        args.append("devices");
        m_process.start(m_adbPath, args);

        ret = true;
    }

    return ret;
}
//adb devicesの結果をパースする
QStringList QMLAdb::parseDevices(QString str)
{
    QStringList ret;
    QStringList temp;
#if defined(Q_OS_WIN)
    temp.append(str.split("\r\n"));
#else
    temp.append(str.split("\n"));
#endif
    bool start_list = false;
    for(int i=0; i<temp.length(); i++){
        if(!start_list){
            if(temp.at(i).indexOf("List of devices attached") == 0){
                //見つけた
                start_list = true;
            }
        }else if(temp.at(i).length() < 1){
            //空行は無視
        }else{
            ret.append(temp.at(i));
        }
    }
    return ret;
}

//adb pushする
bool QMLAdb::postAdbPush(const QString &localFile, const QString &remoteFile)
{
    qDebug() << "QMLAdb::postAdbPush " << localFile << "," << remoteFile << "," << fixPathChar(localFile);

    bool ret = false;
    if(m_state != Idle){
        qDebug() << "  already executing.";
    }else if(m_adbPath.length() < 1){
        qDebug() << "  empty adb path.";
    }else{
        QStringList args;

        m_state = Executing;
        m_mode = Push;
        if(m_deviceName.length() > 0){
            args.append("-s");
            args.append(m_deviceName);
        }
        args.append("push");
        args.append(fixPathChar(localFile));
        args.append(remoteFile);
        m_process.start(m_adbPath, args);

        ret = true;
    }
    return ret;
}
//adb pushの結果をパースする
QStringList QMLAdb::parsePush(QString str)
{
    qDebug() << "QMLAdb::parsePush";

    QStringList ret;
    QStringList temp;
    temp = str.split("\r\n");
    for(int i=0; i<temp.length(); i++){
        if(temp.at(i).indexOf("failed to copy") == 0){
            //NG
            ret.append("fail");
        }
    }
    if(ret.length() > 0){
        ret.append("success");
    }
    return ret;
}

//adb reboot
bool QMLAdb::postAdbReboot(const RebootType type)
{
    qDebug() << "QMLAdb::reboot " << type;

    bool ret = false;
    if(m_state != Idle){
        qDebug() << "  already executing.";
    }else if(m_adbPath.length() < 1){
        qDebug() << "  empty adb path.";
    }else{
        QStringList args;

        m_state = Executing;
        m_mode = Reboot;
        if(m_deviceName.length() > 0){
            args.append("-s");
            args.append(m_deviceName);
        }
        switch(type){
        default:
        case RebootNormal:
            args.append("reboot");
            break;
        case RebootBootLoader:
            args.append("reboot-bootloader");
            break;
        case RebootRecovery:
            args.append("reboot-recovery");
            break;
        }
        m_process.start(m_adbPath, args);

        ret = true;
    }
    return ret;
}

//adb shell mkdirをする
bool QMLAdb::postMkDir(const QString &remoteDir)
{

    qDebug() << "QMLAdb::parsePush " << remoteDir;

    bool ret = false;
    if(m_state != Idle){
        qDebug() << "  already executing.";
    }else if(m_adbPath.length() < 1){
        qDebug() << "  empty adb path.";
    }else{
        QStringList list;
        list = remoteDir.split("/");
        m_tempMkDir.clear();
        for(int i=0; i<list.length(); i++){
    //        qDebug() << i << ":" << list.at(i);
            if(list.at(i).length() == 0){
                //空白が先頭にくる
            }else if(m_tempMkDir.length() == 0){
                m_tempMkDir.append("/" + list.at(i));
            }else{
                m_tempMkDir.append(m_tempMkDir.last() + "/" + list.at(i));
            }
        }

        ret = postMkDir();
    }
    return ret;
}
//adb shell mkdirの繰り返し用
bool QMLAdb::postMkDir()
{
    qDebug() << "QMLAdb::postMkDir2 ";

    bool ret = false;
    if(m_tempMkDir.length() > 0){
//        QString base = "てすてす";
//        QString temp = "";
//        QByteArray wide = base.toLocal8Bit();
//        for(int i=0;i<wide.length();i++){
//            temp += QString("%1").arg(wide.at(i), 0, 16) + ", ";
//        }
//        qDebug() << "wide=" << temp;
        QTextCodec *codec = QTextCodec::codecForName( "UTF-8" );
        QByteArray utf8 = codec->fromUnicode(m_tempMkDir.at(0));
//        QByteArray utf8 = codec->fromUnicode(base);
//        temp = "";
//        for(int i=0;i<utf8.length();i++){
//            temp += QString("%1").arg(utf8.at(i), 0, 16) + ", ";
//        }
//        qDebug() << "utf-8=" << temp;


        QStringList args;
        m_state = Executing;
        m_mode = ShellMkDir;
        if(m_deviceName.length() > 0){
            args.append("-s");
            args.append(m_deviceName);
        }
        args.append("shell");
        args.append("mkdir");
//        args.append(QString::fromUtf8(utf8, utf8.length()));//m_tempMkDir.at(0));
        args.append(m_tempMkDir.at(0));
        m_process.start(m_adbPath, args);

        //作ったのを消す
        m_tempMkDir.removeAt(0);
        ret = true;
    }
    return ret;
}

//adb shellをする
bool QMLAdb::postShellCommand(const QString &command)
{
    qDebug() << "QMLAdb::postShellCommand";

    bool ret = false;
    if(m_state != Idle){
        qDebug() << "  already executing.";
    }else if(m_adbPath.length() < 1){
        qDebug() << "  empty adb path.";
    }else{
        QStringList args;

        m_state = Executing;
        m_mode = ShellCommand;
        m_tempWriteData = command.toUtf8();
        if(m_deviceName.length() > 0){
            args.append("-s");
            args.append(m_deviceName);
        }
        args.append("shell");
        m_process.start(m_adbPath, args);

        ret = true;
    }
    return ret;
}
